<template>
  <div class="frappe-form">
    <form-actions
      class="p-3 border-bottom"
      v-if="shouldRenderForm"
      :doc="doc"
      :links="links"
      @save="save"
      @submit="submit"
      @revert="revert"
      @print="print"
    />
    <form-layout
      class="p-3"
      v-if="shouldRenderForm"
      :doc="doc"
      :fields="meta.fields"
      :layout="meta.layout"
      :invalid="invalid"
    />
    <not-found v-if="notFound" />
  </div>
</template>
<script>
import frappe from 'frappejs';
import FormLayout from './FormLayout';
import FormActions from './FormActions';
import { _ } from 'frappejs/utils';

export default {
  name: 'Form',
  props: ['doctype', 'name', 'defaultValues'],
  components: {
    FormActions,
    FormLayout
  },
  data() {
    return {
      docLoaded: false,
      notFound: false,
      invalid: false,
      invalidFields: [],
      links: [],
      defaults: this.defaultValues
    };
  },
  computed: {
    meta() {
      return frappe.getMeta(this.doctype);
    },
    shouldRenderForm() {
      return this.name && this.docLoaded;
    }
  },
  async created() {
    if (!this.defaults) {
      this.defaults = {};
    }
    this.meta.fields.forEach(field => {
      if (field.defaultValue)
        this.defaults[field.fieldname] = field.defaultValue;
    });
    if (!this.name) return;
    try {
      this.doc = await frappe.getDoc(this.doctype, this.name);

      if (
        this.doc.isNew() &&
        this.meta.fields.map(df => df.fieldname).includes('name')
      ) {
        // For a user editable name field,
        // it should be unset since it is autogenerated
        this.doc.set('name', '');
      }

      if (this.doc.isNew() && this.defaults) {
        for (let fieldname in this.defaults) {
          const value = this.defaults[fieldname];
          await this.doc.set(fieldname, value);
        }
      }

      this.docLoaded = true;
    } catch (e) {
      console.error(e);
      this.notFound = true;
    }
    this.setLinks();
    this.doc.on('change', this.setLinks);
  },
  methods: {
    async save() {
      this.setValidity();
      if (this.invalid) return;

      try {
        if (this.doc.isNew()) {
          await this.doc.insert();
        } else {
          await this.doc.update();
        }

        this.$emit('save', this.doc);
      } catch (e) {
        console.error(e);
        return;
      }
    },

    setLinks() {
      if (this.meta.links) {
        let links = [];
        for (let link of this.meta.links) {
          if (link.condition(this)) {
            link.handler = () => {
              link.action(this);
            };
            links.push(link);
          }
        }
        this.links = links;
      }
    },

    async submit() {
      await this.doc.set('submitted', 1);
      try {
        await this.save();
      } catch (e) {
        await this.doc.set('submitted', 0);
        await this.doc.set('_dirty', false);
      }
    },

    async revert() {
      await this.doc.set('submitted', 0);
      try {
        await this.save();
      } catch (e) {
        await this.doc.set('submitted', 1);
        await this.doc.set('_dirty', false);
      }
    },

    print() {
      this.$router.push(`/print/${this.doctype}/${this.name}`);
    },

    onValidate(fieldname, isValid) {
      if (!isValid && !this.invalidFields.includes(fieldname)) {
        this.invalidFields.push(fieldname);
      } else if (isValid) {
        this.invalidFields = this.invalidFields.filter(
          invalidField => invalidField !== fieldname
        );
      }
    },

    setValidity() {
      const form = this.$el.querySelector('form');
      let validity = form.checkValidity();
      this.invalid = !validity;
    }
  }
};
</script>
<style>
</style>
